namespace FooVision {

    using System;
    using System.IO;
    using System.Net;
    using System.Collections.Specialized;
    using System.Runtime.InteropServices;
    using Btctl;
    using Gtk;
    using GConf;

    public class Client {

        static string FOOVISION_PATH = "/apps/foovision";
        static string USER_KEY = FOOVISION_PATH + "/user";
        static string PASSWORD_KEY = FOOVISION_PATH + "/password";
        static string SERVER_KEY = FOOVISION_PATH + "/server";

        Entry userEntry;
        Entry passEntry;
        Entry serverEntry;
        GConf.Client gconf;
        Obex obex;

        public static void Main (string[] args)
        {
            Application.Init ();
            Client c = new Client();
            Application.Run ();
        }

        public Client()
        {
            // OBEX
            obex = new Obex ();
            obex.RequestPut += Put_Request;
            obex.Put += Put;
            obex.Error += Error;
            obex.Progress += Progress;
            obex.Complete += Complete;

            if (obex.IsInitialised) {
                Console.WriteLine ("OBEX Initialised OK.");
            } else {
                Console.WriteLine ("OBEX not initialised, exiting.");
            }

            // GConf
            gconf = new GConf.Client();
            gconf.AddNotify (FOOVISION_PATH, new NotifyEventHandler (GConfChanged));

            // Tray Icon
            EventBox eb = new EventBox();
            eb.Add(Image.LoadFromResource("foovision.png"));
            eb.ButtonPressEvent += OnImageClick;
            Egg.TrayIcon icon = new Egg.TrayIcon("FooVision");
            icon.Add(eb);
            icon.ShowAll();
        }


        private void OnImageClick (object o, ButtonPressEventArgs args) 
        {
            if (args.Event.Button == 3) // Right click
            {
                Menu popupMenu = new Menu(); 

                // Preferences
                ImageMenuItem opPreferences = new ImageMenuItem ("Preferences");
                opPreferences.Image = new Image(Stock.Preferences, IconSize.Menu);
                popupMenu.Add(opPreferences); 
                opPreferences.Activated += OnPreferencesClick;
                popupMenu.ShowAll(); 

                // Quit
                ImageMenuItem opQuit = new ImageMenuItem ("Quit");
                opQuit.Image = new Image(Stock.Quit, IconSize.Menu);
                popupMenu.Add(opQuit); 
                opQuit.Activated += OnQuitClick;
                popupMenu.ShowAll(); 

                popupMenu.Popup();
            }
        }

        private void OnPreferencesClick(object o, EventArgs args)
        {
            Dialog dialog = new Dialog ();
            dialog.Title = "Foovision Preferences";
            dialog.Modal = true;

            HBox h = new HBox();
            h.BorderWidth = 6;
            h.Spacing = 6;

            VBox v = new VBox();
            v.Spacing = 6;

            Label l = new Label("User:");
            l.Xalign = 0;
            v.PackStart(l, true, false, 0);

            l = new Label("Password:");
            l.Xalign = 0;
            v.PackStart(l, true, false, 0);

            l = new Label("Server:");
            l.Xalign = 0;
            v.PackStart(l, true, false, 0);

            h.PackStart(v, false, false, 0);

            v = new VBox();
            v.Spacing = 6;

            userEntry = new Entry();
            userEntry.WidthChars = 25;
            v.PackStart(userEntry, true, true, 0);

            passEntry = new Entry();
            passEntry.WidthChars = 25;
            passEntry.Visibility = false;
            v.PackStart(passEntry, true, true, 0);

            serverEntry = new Entry();
            serverEntry.WidthChars = 25;
            v.PackStart(serverEntry, true, true, 0);

            h.PackStart(v, true, true, 0);

            dialog.VBox.PackStart (h, false, false, 0);
            dialog.VBox.ShowAll ();

            // Update entries from GConf
            UpdateFromGConf();

            dialog.AddButton (Stock.Cancel, ResponseType.Cancel);
            dialog.AddButton (Stock.Apply, ResponseType.Apply);
            dialog.Response += new ResponseHandler (OnDialogResponse);
            dialog.Run ();
            dialog.Destroy ();

        }

        private string GetKey (string k)
        {
            string v = "";
            try {
                try {
                    v = (string) gconf.Get(k);
                } catch (GConf.NoSuchKeyException e) {
                    if (k == SERVER_KEY) {
                       v = "http://foovision.org/receiver"; 
                    }
                    gconf.Set(k, v);
                }
            } catch (System.InvalidCastException e) {
                Console.WriteLine("Error: Cannot typecast.");
            }
            return v;
        }

        private void UpdateFromGConf ()
        {
            userEntry.Text = GetKey(USER_KEY);
            passEntry.Text = GetKey(PASSWORD_KEY);
            serverEntry.Text = GetKey(SERVER_KEY);
        }

        private void GConfChanged (object sender, NotifyEventArgs args)
        {
            UpdateFromGConf();
        }

        private void OnDialogResponse (object obj, ResponseArgs args)
        {
            if (args.ResponseId == ResponseType.Apply) {
                gconf.Set (USER_KEY, userEntry.Text);
                gconf.Set (PASSWORD_KEY, passEntry.Text);
                gconf.Set (SERVER_KEY, serverEntry.Text);
                Console.WriteLine ("Preferences saved");
            }
        }

        private void OnQuitClick (object o, EventArgs args)
        {
            Console.WriteLine("Quit");
            Application.Quit();
        }


        private void Progress (object obj, ProgressArgs args)
        {
            Console.WriteLine ("Progress from {0}", args.Bdaddr);
        }

        private void Complete (object obj, CompleteArgs args)
        {
            Console.WriteLine ("Complete from {0}", args.Bdaddr);
        }

        private void Put_Request (object obj, RequestPutArgs args)
        {
            Console.WriteLine ("Put request from {0}", args.Bdaddr);
            ((Obex)obj).Response = true;
        }

        private void Put (object obj, PutArgs args)
        {
            string temporalFile = "/tmp/" + args.Fname;	
            string sender = args.Bdaddr; // TODO: Get preferred name.
            Console.WriteLine ("Saving file {0}  from {1} with length {2}",
            temporalFile, args.Bdaddr, args.Data.BodyLen);

            FileStream stream = new FileStream(temporalFile, FileMode.OpenOrCreate);
            stream.Write(args.Data.Body, 0, args.Data.Body.Length);
            stream.Close();	

            // Post File
            PostFile(sender, temporalFile);

            ((Obex)obj).Response = true;
        }

        private void Error (object o, ErrorArgs args)
        {
            Console.WriteLine ("Error from {0}, code {1}",
            args.Bdaddr, args.Code);
        }

        private bool PostFile(string sender, string file)
        {

            Console.WriteLine ("Posting file {0} from {1}", file, sender);
            try {
                HttpWebRequest req = (HttpWebRequest) WebRequest.Create(GetKey(SERVER_KEY));
                string boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");
                req.Method = "POST";
                req.ContentType = "multipart/form-data; boundary=" + boundary;
                req.KeepAlive = true;
                req.Credentials = System.Net.CredentialCache.DefaultCredentials;

                Stream memStream = new System.IO.MemoryStream();

                byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");

                string formdataTemplate = "\r\n--" + boundary + "\r\nContent-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}";


                NameValueCollection queryString = new NameValueCollection();
                queryString["username"] = GetKey(USER_KEY);
                queryString["password"] = GetKey(PASSWORD_KEY);
                queryString["sender"]   = sender;

                foreach(string key in queryString.Keys) {
                    string formitem = string.Format(formdataTemplate, key, queryString[key]);
                    byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
                    memStream.Write(formitembytes, 0, formitembytes.Length);
                }

                memStream.Write(boundarybytes, 0, boundarybytes.Length);

                string headerTemplate = "Content-Disposition: form-data; name=\"{0}\";filename=\"{1}\"\r\nContent-Type: application/octet-stream\r\n\r\n";

                string header = string.Format(headerTemplate, "file", file);
                byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
                memStream.Write(headerbytes, 0, headerbytes.Length);

                FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read);
                byte[] buffer = new byte[1024];

                int bytesRead = 0;
                while ( (bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0 ) {
                    memStream.Write(buffer, 0, bytesRead);
                }

                memStream.Write(boundarybytes,0,boundarybytes.Length);
                fileStream.Close();

                req.ContentLength = memStream.Length;

                Stream requestStream = req.GetRequestStream();

                memStream.Position = 0;
                byte[] tempBuffer = new byte[memStream.Length];
                memStream.Read(tempBuffer,0,tempBuffer.Length);
                memStream.Close();
                requestStream.Write(tempBuffer,0,tempBuffer.Length );
                requestStream.Close();

                WebResponse res = req.GetResponse();
                Stream stream2 = res.GetResponseStream();
                StreamReader reader2 = new StreamReader(stream2);

                Console.WriteLine(reader2.ReadToEnd());

                res.Close();
                req = null;
                res = null;
            } catch(WebException e) {
                Console.WriteLine("Exception Message:\n" + e.Message);
                if(e.Status == WebExceptionStatus.ProtocolError) {
                    Console.WriteLine("\nStatus Code: {0}", ((HttpWebResponse)e.Response).StatusCode);
                    Console.WriteLine("\nStatus Description : {0}\n", ((HttpWebResponse)e.Response).StatusDescription);
                }
            } catch(Exception e) {
                Console.WriteLine(e.Message);
            }

            return true;
        }
    }
}
